package com.smat.taglib.html;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public abstract class Element<T extends Element<T>>
  implements Node
{
  private String tagName;
  private boolean selfClosing;
  private List<Node> children;
  private Map<String, Object> attributes;
  private String innerHtml;

  protected Element(String tagName)
  {
    this(tagName, false);
  }

  protected Element(String tagName, boolean selfClosing) {
    this.children = new ArrayList();
    this.innerHtml = "";
    this.tagName = tagName;
    this.selfClosing = selfClosing;
    this.attributes = new HashMap();
  }

  public T append(Node child)
  {
    this.children.add(child);

    return (T)this;
  }

  public T text(String text) {
    return html(Text.escapeHtml(text));
  }

  public T html(String html)
  {
    this.innerHtml = html;

    this.children.clear();

    return (T)this;
  }

  public String html() {
    return this.innerHtml;
  }

  public T attr(String attribute, Object value)
  {
    this.attributes.put(attribute, value);

    return (T)this;
  }

  public void write(Writer out) throws IOException {
    out.append("<").append(this.tagName);

    for (Map.Entry attribute : this.attributes.entrySet()) {
      out.append(" ").append((CharSequence)attribute.getKey()).append("=\"").append(attribute.getValue().toString()).append("\"");
    }

    if (this.selfClosing) {
      out.append(" />");
    } else {
      out.append(">");

      writeContent(out);

      out.append("</").append(this.tagName).append(">");
    }
  }

  private void writeContent(Writer out)
    throws IOException
  {
    if (!this.innerHtml.isEmpty()) {
      out.write(this.innerHtml);
    }

    for (Node child : this.children)
      child.write(out);
  }

  public String outerHtml() throws IOException
  {
    StringWriter out = new StringWriter();

    write(out);

    return out.toString();
  }
}